<?php
/*
 * 加载类
 */
class Loader{
	/**
	 * List of loaded base classes
	 * Set by the controller class
	 *
	 * @var array
	 * @access protected
	 */
	protected $_base_classes		= array(); // Set by the controller class	
	/**
	 * List of loaded files
	 *
	 * @var array
	 * @access protected
	 */
	protected $_ci_loaded_files		= array();	
	
	protected $_ci_view_paths		= array();
	
	protected $_ci_cached_vars		= array();
	/**
	 * 构造函数
	 *
	 * 设置默认变量
	 */
	public function __construct()
	{
		$this->_ci_ob_level  = ob_get_level();
		$this->_ci_library_paths = array(APPPATH, FPATH);
		$this->_ci_helper_paths = array(APPPATH, FPATH);
		$this->_ci_model_paths = array(APPPATH);
		$this->_ci_view_paths = array(APPPATH.'views/'	=> TRUE);
	//	log_message('debug', "Loader Class Initialized");
	
	}
	// --------------------------------------------------------------------
	
	/**
	 * 初始化自动加载的类
	 * @param 	array
	 * @return 	object
	 */
	public function initialize()
	{
		$this->_ci_classes = array();
		$this->_ci_loaded_files = array();
		$this->_ci_models = array();
		$this->_base_classes =& is_loaded();
	
		$this->_autoloader();
	
		return $this;
	}		
	// --------------------------------------------------------------------
	private function _autoloader(){
		include(APPPATH.'config/autoload.php');	//获取自动加载配置文件
		
		if ( ! isset($autoload))
		{
			return FALSE;
		}		
		//print_r($autoload);
		// Autoload packages
// 		if (isset($autoload['packages']))
// 		{
// 			foreach ($autoload['packages'] as $package_path)
// 			{
// 				$this->add_package_path($package_path);
// 			}
// 		}		
		// Load libraries
		if (isset($autoload['libraries']) AND count($autoload['libraries']) > 0)
		{
			// Load the database driver.
			if (in_array('database', $autoload['libraries']))
			{
				$this->database();
				$autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
			}
		
			// Load all other libraries
			foreach ($autoload['libraries'] as $item)
			{
				$this->library($item);
			}
		}
		
		// Autoload models
		if (isset($autoload['model']))
		{
			$this->model($autoload['model']);
		}		
	}	
	
	/**
	 * 加载类库
	 */
	public function library($library = '', $params = NULL, $object_name = NULL){
		//递归调用
		if (is_array($library))
		{
			foreach ($library as $class)
			{
				$this->library($class, $params);
			}
		
			return;
		}
		if ($library == '' OR isset($this->_base_classes[$library]))
		{
			return FALSE;
		}		
		//递归调用		
		$arrClassName=$this->_load_class('libraries',$library, $params, $object_name);	//载入类
		
		$this->_init_class($arrClassName[0], $arrClassName[1], $params, $object_name);				//初始化类
	}
	// --------------------------------------------------------------------
	
	/**
	 * 数据模型加载
	 */
	public function model($model, $name = '', $db_conn = FALSE)
	{
		if (is_array($model))
		{
			foreach ($model as $babe)
			{
				$this->model($babe);
			}
			return;
		}
	
		if ($model == '')
		{
			return;
		}
		$arrClassName=$this->_load_class('models',$model,null, $name);	//载入类	
		$this->_init_class(ucfirst($arrClassName[0]), $arrClassName[1], null, $name);
		return;
	
		// couldn't find the model
	//	show_error('Unable to locate the model you have specified: '.$model);
	}	
	// --------------------------------------------------------------------
	
	/**
	 * 加载视图
	 */
	public function view($view, $vars = array(), $return = FALSE)
	{
		return $this->_load_view(array('_ci_view' => $view, '_ci_vars' => $this->_object_to_array($vars), '_ci_return' => $return));
	}	
	// --------------------------------------------------------------------
	/**
	 * 视图加载
	 */
	
	protected function _load_view($_ci_data)
	{
		foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
		{
			$$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val];
		}
	
		$file_exists = FALSE;
	
		// Set the path to the requested file
		if ($_ci_path != '')
		{
			$_ci_x = explode('/', $_ci_path);
			$_ci_file = end($_ci_x);
		}
		else
		{
			$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
			$_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view;
			foreach ($this->_ci_view_paths as $view_file => $cascade)
			{
				if (file_exists($view_file.$_ci_file))
				{
					$_ci_path = $view_file.$_ci_file;
					$file_exists = TRUE;
					break;
				}
	
				if ( ! $cascade)
				{
					break;
				}
			}
		}
		
		if ( ! $file_exists && ! file_exists($_ci_path))
		{
		//	show_error('Unable to load the requested file: '.$_ci_file);
		}
	
		// This allows anything loaded using $this->load (views, files, etc.)
		// to become accessible from within the Controller and Model functions.
	
		$LK =& get_instance();
		foreach (get_object_vars($LK) as $_ci_key => $_ci_var)
		{
			if ( ! isset($this->$_ci_key))
			{
				$this->$_ci_key =& $LK->$_ci_key;
			}
		}
	
		if (is_array($_ci_vars))
		{
			$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
		}
		extract($this->_ci_cached_vars);
	
		/*
		 * Buffer the output
		*/
		ob_start();
	
		if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
		{
			echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
		}
		else
		{
			include($_ci_path); // include() vs include_once() allows for multiple views with the same name
		}
	
		log_message('debug', 'File loaded: '.$_ci_path);
	
		// Return the file data if requested
		if ($_ci_return === TRUE)
		{
			$buffer = ob_get_contents();
			@ob_end_clean();
			return $buffer;
		}
	
		/*
		 * Flush the buffer... or buff the flusher?
		*
		*/
		if (ob_get_level() > $this->_ci_ob_level + 1)
		{
			ob_end_flush();
		}
		else
		{
			$LK->output->append_output(ob_get_contents());
			@ob_end_clean();
		}
	}
	
	// --------------------------------------------------------------------	
	
	/**
	 * 类加载函数
	 */
	protected function _load_class($directory,$library, $params = NULL, $object_name = NULL)
	{		
		if ( ! is_null($params) && ! is_array($params))
		{
			$params = NULL;
		}			
		
		$class = str_replace('.php', '', trim($library, '/'));
		$is_duplicate = FALSE;

		$subdir = '';
		if (($last_slash = strrpos($class, '/')) !== FALSE)
		{
			// Extract the path
			$subdir = substr($class, 0, $last_slash + 1);
			// Get the filename from the path
			$class = substr($class, $last_slash + 1);
		}
		$class = ucfirst($class);//所有类首字母大写
		if($directory=='libraries'){
			$subclass = APPPATH.$directory.'/'.$subdir.config_item('subclass_prefix').$class.'.php';
			$baseclass = FPATH.$directory.'/'.$class.'.php';
			if( file_exists($subclass)   ){	//使用子类前缀的是在lookphp目录下有父类
				$is_duplicate=$this->_object_check($subclass,$object_name);	//判断是否属于重复加载
				if($is_duplicate == FALSE){
					if(file_exists($baseclass)){
						include_once($baseclass);
						include_once($subclass);
						$this->_ci_loaded_files[]=$subclass;
						return array(0=>$class,1=>config_item('subclass_prefix'));
				//		return $this->_init_class($class, config_item('subclass_prefix'), $params, $object_name);
					}	
				}				
			}
		}
		foreach ($this->_ci_library_paths as $path){//应用程序目录与框架目录的类
			$filepath = $path.$directory.'/'.$subdir.$class.'.php';
			if (file_exists($filepath)){	
				$is_duplicate=$this->_object_check($filepath,$object_name);	//判断是否属于重复加载
				if($is_duplicate == FALSE){
					include_once($filepath);
					$this->_ci_loaded_files[]=$filepath;
					return array(0=>$class,1=>'');//返回前缀
				//	return $this->_init_class($class, '', $params, $object_name);
						
				}
			}			
			
		}
		if($is_duplicate == TRUE){
			log_message('debug', $class." class already loaded. Second attempt ignored.");
			return;
		}
		// One last attempt.  Maybe the library is in a subdirectory, but it wasn't specified?
		if ($subdir == '')
		{
			$path = strtolower($class).'/'.$class;
			return $this->_load_class($path, $params);
		}
	
		// If we got this far we were unable to find the requested class.
		// We do not issue errors if the load call failed due to a duplicate request
		if ($is_duplicate == FALSE)
		{
			log_message('error', "Unable to load the requested class: ".$class);
			//	show_error("Unable to load the requested class: ".$class);
		}
	}	
	/*
	 * 判断是否属于重复加载
	 */
	public function _object_check($class_path,$object_name){
		if (in_array($class_path, $this->_ci_loaded_files))
		{
			if ( ! is_null($object_name))
			{
				$LK =& get_instance();
				if ( isset($LK->$object_name))
				{
					return TRUE;
				}
			}else{
				return TRUE;
			}
				
		}
		return FALSE;
				
	}	
	// --------------------------------------------------------------------
	
	/**
	 * 初始化指定类
	 */
	protected function _init_class($class, $prefix = '', $config = FALSE, $object_name = NULL)
	{
		if ($config === NULL)
		{
			// Fetch the config paths containing any package paths
			$config_component = $this->_ci_get_component('config');
			
			if (is_array($config_component->_config_paths))
			{
				// Break on the first found file, thus package files
				// are not overridden by default paths
				foreach ($config_component->_config_paths as $path)
				{
					
					// We test for both uppercase and lowercase, for servers that
					// are case-sensitive with regard to file names. Check for environment
					// first, global next
					if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
					{
						include($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
						break;
					}
					elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
					{
						include($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
						break;
					}
					elseif (file_exists($path .'config/'.strtolower($class).'.php'))
					{
						include($path .'config/'.strtolower($class).'.php');
						break;
					}
					elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php'))
					{
						include($path .'config/'.ucfirst(strtolower($class)).'.php');
						break;
					}
				}
			}
		}		
			$name = $prefix.$class;
		// Is the class name valid?
		if ( ! class_exists($name))
		{
			log_message('error', "Non-existent class: ".$name);
		//	show_error("Non-existent class: ".$class);
		}
	
		// Set the variable name we will assign the class to
		// Was a custom class name supplied?  If so we'll use it
		//$class = strtolower($class);
	
		if (is_null($object_name))
		{
			$classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class];
		}
		else
		{
			$classvar = $object_name;
		}
	
		// Save the class name and object name
		$this->_ci_classes[$class] = $classvar;
	
		// Instantiate the class
		$LK =& get_instance();
		if ($config !== NULL)
		{
			$LK->$classvar = new $name($config);
		}
		else
		{
			$LK->$classvar = new $name;
		}
	}
	// --------------------------------------------------------------------
	
	/**
	 * Database Loader
	 *
	 * @param	string	the DB credentials
	 * @param	bool	whether to return the DB object
	 * @param	bool	whether to enable active record (this allows us to override the config setting)
	 * @return	object
	 */
	public function database($params = '', $return = FALSE, $active_record = NULL)
	{
		// Grab the super object
		$LK =& get_instance();
	
		// Do we even need to load the database class?
		if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($LK->db) AND is_object($LK->db))
		{
			return FALSE;
		}
		if (is_string($params) AND strpos($params, '://') === FALSE){		//没有传入数据库服务器信息则载入默认配置
			$file_path = APPPATH.'config/database.php';
			include($file_path);
			if ($params != '')
			{
				$active_group = $params;
			}
			$params = $db[$active_group];
		}elseif (is_string($params))
		{
			if (($dns = @parse_url($params)) === FALSE)
			{
			//	show_error('Invalid DB Connection String');
			}
			
			$params = array(
					'dbdriver'	=> $dns['scheme'],
					'hostname'	=> (isset($dns['host'])) ? rawurldecode($dns['host']) : '',
					'username'	=> (isset($dns['user'])) ? rawurldecode($dns['user']) : '',
					'password'	=> (isset($dns['pass'])) ? rawurldecode($dns['pass']) : '',
					'database'	=> (isset($dns['path'])) ? rawurldecode(substr($dns['path'], 1)) : ''
			);	
			// were additional config items set?
			if (isset($dns['query']))
			{
				parse_str($dns['query'], $extra);
			
				foreach ($extra as $key => $val)
				{
					// booleans please
					if (strtoupper($val) == "TRUE")
					{
						$val = TRUE;
					}
					elseif (strtoupper($val) == "FALSE")
					{
						$val = FALSE;
					}
			
					$params[$key] = $val;
				}
			}					
		}
	
	require_once(BASEPATH.'database/DB_driver.php');

	if ( ! isset($active_record) OR $active_record == TRUE)
	{
		require_once(BASEPATH.'database/DB_active_rec.php');

		if ( ! class_exists('CI_DB'))
		{
			eval('class CI_DB extends CI_DB_active_record { }');
		}
	}
	else
	{
		if ( ! class_exists('CI_DB'))
		{
			eval('class CI_DB extends CI_DB_driver { }');
		}
	}

	require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php');

	// Instantiate the DB adapter
	$driver = 'CI_DB_'.$params['dbdriver'].'_driver';
	$DB = new $driver($params);

	if ($DB->autoinit == TRUE)
	{
		$DB->initialize();
	}
	
	if (isset($params['stricton']) && $params['stricton'] == TRUE)
	{
		$DB->query('SET SESSION sql_mode="STRICT_ALL_TABLES"');
	}	
		// Initialize the db variable.  Needed to prevent
		// reference errors with some configurations
		$LK->db = '';
	
		// Load the DB class
		$LK->db =&$DB;
	}	
	// --------------------------------------------------------------------
	
	/**
	 * Object to Array
	 *
	 * Takes an object as input and converts the class variables to array key/vals
	 *
	 * @param	object
	 * @return	array
	 */
	protected function _object_to_array($object)
	{
		return (is_object($object)) ? get_object_vars($object) : $object;
	}

	/**
	 * Get a reference to a specific library or model
	 *
	 * @param 	string
	 * @return	bool
	 */
	protected function &_ci_get_component($component)
	{
		$LK =& get_instance();
		return $LK->$component;
	}		
}